/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.iss.card;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map.Entry;
import java.util.WeakHashMap;

import android.content.Context;
import android.util.Pair;

public class CardController {

    // This uses a LinkedHashMap so that we can replace fragments based on the
    // view id they are being expanded into since we can't guarantee a reference
    // to the handler will be findable
    private LinkedHashMap<Integer,EventHandler> eventHandlers =
            new LinkedHashMap<Integer,EventHandler>(5);
    private LinkedList<Integer> mToBeRemovedEventHandlers = new LinkedList<Integer>();
    private LinkedHashMap<Integer, EventHandler> mToBeAddedEventHandlers = new LinkedHashMap<
            Integer, EventHandler>();
    private Pair<Integer, EventHandler> mFirstEventHandler;
    private Pair<Integer, EventHandler> mToBeAddedFirstEventHandler;
    private volatile int mDispatchInProgressCounter = 0;

    private static WeakHashMap<Context, CardController> instances =
        new WeakHashMap<Context, CardController>();


    private int mViewType = -1;
    private int mPreviousViewType = -1;
    private long mEventId = -1;
    private long mDateFlags = 0;

    /**
     * One of the event types that are sent to or from the controller
     */
    public interface EventType {
    	/**閿熸暀瀹剁鎷烽敓鏂ゆ嫹*/
    	final long STORE_CONTENT = 1L;
    	
    	final long ACCOUNT_CONTENT = 1L << 1;
    	
    	final long CARDBAG_CONTENT = 1L << 2;
    	
    	final long COLLENT_CONTENT = 1L << 3;
    	
    	final long VIP_CARD = 1L << 4;
    	
    	final long HISTORY_TRACE = 1L << 5;
    	
    	final long VIP_RULE = 1L << 6;
    	
    	final long SHOP_EXHIBITION = 1L << 7;
    	/**閿熸枻鎷峰綍椤甸敓鏂ゆ嫹 閿熸枻鎷烽敓鏂ゆ嫹娉ㄩ敓鏂ゆ嫹*/
    	final long ACCOUNT_REGISTER = 1L << 8;
    	
    	final long CHANGE_PASSWORD = 1L << 9;
    	
    	final long ACTION_RETURN = 1L << 10;
    	
    	final long ACTION_LOGIN = 1L << 11;
    	
    	final long FORGET_PASSWORD = 1L << 12;
    	
    	final long ACCOUNT_LOGOUT = 1L << 13;
    	
    	final long ACCOUNT_LOGIN = 1L << 14;
    	
    	final long ACCOUNT_TITLE_ACCOUNT = 1L << 15;
    	/**閿熸枻鎷峰綍椤甸敓鏂ゆ嫹 title 閿熸枻鎷烽敓鏂ゆ嫹閿熸枻鎷烽敓锟�
    	
    	/**娉ㄩ敓鏂ゆ嫹椤甸敓鏂ゆ嫹 title 娉ㄩ敓鏂ゆ嫹*/
    	final long ACCOUNT_TITLE_LOGIN = 1L << 16;
    	final long ACCOUNT_TITLE_REGISTER = 1L << 17;
    	
    	final long ACCOUNT_TITLE_CHANGE_PAS = 1L << 18;
    	
    	final long ACCOUNT_DO_REGISTER = 1L << 19;
    	
    	final long ACCOUNT_SEND_MESSAGE = 1L << 20;
    	/**閿熸枻鎷烽敓鏂ゆ嫹閿熸枻鎷烽敓锟介〉閿熸枻鎷�/
    	 * /**閿熸枻鎷烽敓鏂ゆ嫹閿熷彨鎲嬫嫹*/
    	
    	final long ACCOUNT_FIND_PASSWORD = 1L << 21;
    	
    	final long ACCOUNT_SHAKE_RESUTIL = 1L << 22;
    	
    	final long ACCOUNT_SHAKE_GO_LOOK = 1L << 23;
    	
    	final long ACCOUNT_CHANGE_PASSWORD = 1L << 24;
    	
    	final long ACCOUNT_RETURN_SHAKE = 1L << 25;
    	
    	final long ACCOUNT_DELETE_ALL_FRAGMENT = 1L << 26;
    	
    	final long ACCOUNT_DELETE_FRAGMENT = 1L << 27;
    	
    	final long ACCOUNT_CITYLIST = 1L << 28;
    	
    	final long STORE_FILTER = 1L << 29;
    	
    	final long UNPAY_ORDER_LIST = 1L <<30;
    }

    public static class EventInfo {
    	
        public long eventType; // one of the EventType
        
        public Object values; // pass by value
    }

    public interface EventHandler {
        long getSupportedEventTypes();
        void handleEvent(EventInfo event);

        /**
         * This notifies the handler that the database has changed and it should
         * update its view.
         */
        void eventsChanged();
    }

    /**
     * Creates and/or returns an instance of CardController associated with
     * the supplied context. It is best to pass in the current Activity.
     *
     * @param context The activity if at all possible.
     */
    public static CardController getInstance(Context context) {
        synchronized (instances) {
            CardController controller = instances.get(context);
            if (controller == null) {
                controller = new CardController(context);
                instances.put(context, controller);
            }
            return controller;
        }
    }

    /**
     * Removes an instance when it is no longer needed. This should be called in
     * an activity's onDestroy method.
     *
     * @param context The activity used to create the controller
     */
    public static void removeInstance(Context context) {
        instances.remove(context);
    }

    private CardController(Context context) {
    }
    
    /**
     * Send info
     * 
     * @param eventType
     * @param values
     */
    public void sendEvent(long eventType, Object values){
    	EventInfo event = new EventInfo();
    	event.eventType = eventType;
    	event.values = values;
    	
    	this.sendEvent(event);
    }
    
    /**
     * Send info
     * 
     * @param eventType
     */
    public void sendEvent(long eventType){
    	EventInfo event = new EventInfo();
    	event.eventType = eventType;
    	
    	this.sendEvent(event);
    }
    

    public void sendEvent(final EventInfo event) {

        mPreviousViewType = mViewType;

        synchronized (this) {
            mDispatchInProgressCounter ++;

            // Dispatch to event handler(s)
            if (mFirstEventHandler != null) {
                // Handle the 'first' one before handling the others
                EventHandler handler = mFirstEventHandler.second;
                if (handler != null && (handler.getSupportedEventTypes() & event.eventType) != 0
                        && !mToBeRemovedEventHandlers.contains(mFirstEventHandler.first)) {
                    handler.handleEvent(event);
                }
            }
            for (Iterator<Entry<Integer, EventHandler>> handlers =
                    eventHandlers.entrySet().iterator(); handlers.hasNext();) {
                Entry<Integer, EventHandler> entry = handlers.next();
                int key = entry.getKey();
                if (mFirstEventHandler != null && key == mFirstEventHandler.first) {
                    // If this was the 'first' handler it was already handled
                    continue;
                }
                EventHandler eventHandler = entry.getValue();
                if (eventHandler != null
                        && (eventHandler.getSupportedEventTypes() & event.eventType) != 0) {
                    if (mToBeRemovedEventHandlers.contains(key)) {
                        continue;
                    }
                    eventHandler.handleEvent(event);
                }
            }

            mDispatchInProgressCounter --;

            if (mDispatchInProgressCounter == 0) {

                // Deregister removed handlers
                if (mToBeRemovedEventHandlers.size() > 0) {
                    for (Integer zombie : mToBeRemovedEventHandlers) {
                        eventHandlers.remove(zombie);
                        if (mFirstEventHandler != null && zombie.equals(mFirstEventHandler.first)) {
                            mFirstEventHandler = null;
                        }
                    }
                    mToBeRemovedEventHandlers.clear();
                }
                // Add new handlers
                if (mToBeAddedFirstEventHandler != null) {
                    mFirstEventHandler = mToBeAddedFirstEventHandler;
                    mToBeAddedFirstEventHandler = null;
                }
                if (mToBeAddedEventHandlers.size() > 0) {
                    for (Entry<Integer, EventHandler> food : mToBeAddedEventHandlers.entrySet()) {
                        eventHandlers.put(food.getKey(), food.getValue());
                    }
                }
            }
        }
    }

    /**
     * Adds or updates an event handler. This uses a LinkedHashMap so that we can
     * replace fragments based on the view id they are being expanded into.
     *
     * @param key The view id or placeholder for this handler
     * @param eventHandler Typically a fragment or activity in the calendar app
     */
    public void registerEventHandler(int key, EventHandler eventHandler) {
        synchronized (this) {
            if (mDispatchInProgressCounter > 0) {
                mToBeAddedEventHandlers.put(key, eventHandler);
            } else {
                eventHandlers.put(key, eventHandler);
            }
        }
    }

    public void registerFirstEventHandler(int key, EventHandler eventHandler) {
        synchronized (this) {
            registerEventHandler(key, eventHandler);
            if (mDispatchInProgressCounter > 0) {
                mToBeAddedFirstEventHandler = new Pair<Integer, EventHandler>(key, eventHandler);
            } else {
                mFirstEventHandler = new Pair<Integer, EventHandler>(key, eventHandler);
            }
        }
    }

    public void deregisterEventHandler(Integer key) {
        synchronized (this) {
//            if (mDispatchInProgressCounter > 0) {
//                // To avoid ConcurrencyException, stash away the event handler for now.
//                mToBeRemovedEventHandlers.add(key);
//            } else {
                eventHandlers.remove(key);
                if (mFirstEventHandler != null && mFirstEventHandler.first == key) {
                    mFirstEventHandler = null;
                }
//            }
        }
    }

    public void deregisterAllEventHandlers() {
        synchronized (this) {
//            if (mDispatchInProgressCounter > 0) {
//                // To avoid ConcurrencyException, stash away the event handler for now.
//                mToBeRemovedEventHandlers.addAll(eventHandlers.keySet());
//            } else {
                eventHandlers.clear();
                mFirstEventHandler = null;
//            }
        }
    }


    /**
     * @return the last set of date flags sent with
     *         {@link EventType#UPDATE_TITLE}
     */
    public long getDateFlags() {
        return mDateFlags;
    }

    /**
     * @return the last event ID the edit view was launched with
     */
    public long getEventId() {
        return mEventId;
    }

    public int getViewType() {
        return mViewType;
    }

    public int getPreviousViewType() {
        return mPreviousViewType;
    }

    // Forces the viewType. Should only be used for initialization.
    public void setViewType(int viewType) {
        mViewType = viewType;
    }

    // Sets the eventId. Should only be used for initialization.
    public void setEventId(long eventId) {
        mEventId = eventId;
    }
}
